// MapTileManager.js
const sharp = require('sharp');
const fs = require('fs').promises;
const path = require('path');

class MapTileManager {
    /**
     * @param {TileSystem} tileSystem - 切片系统实例
     */
    constructor(app, tileSystem) {
        if (!tileSystem) {
            throw new Error('tileSystem 必须被实例化');
        }
        this.app = app;
        this.tileSystem = tileSystem;
        this.processedTiles = new Set(); // 用于记录已处理的切片
        this.missingTiles = []; // 记录缺失的切片
    }

    /**
     * 读取 screenshots 目录下的所有截图及其元数据
     */
    async loadScreenshots(dir) {
        try {
            const files = await fs.readdir(dir);
            const imageFiles = files.filter((file) => file.endsWith('.png'));
            const screenshots = [];

            for (const imageFile of imageFiles) {
                const imagePath = path.join(dir, imageFile);
                const metadataFile = imageFile.replace('.png', '.json');
                const metadataPath = path.join(dir, metadataFile);

                try {
                    const metadataRaw = await fs.readFile(metadataPath, 'utf-8');
                    const metadata = JSON.parse(metadataRaw);
                    screenshots.push({ imagePath, metadata });
                } catch (err) {
                    console.warn(`无法读取元数据文件: ${metadataPath}, 跳过 ${imageFile}`);
                }
            }

            return screenshots;
        } catch (error) {
            console.error('读取输入目录时出错:', error);
            return [];
        }
    }

    // 获取tile(x,y)对应的中心像素坐标
    getTileCenter(centerLat, centerLng, zoom, imageWidth, imageHeight, x, y, scale) {
        const tileKey = `${zoom}/${x}/${y}`;

        // 获取切片的地理范围
        const tileBounds = this.tileSystem.getTileBounds(x, y, zoom);
        const { nw, se } = tileBounds;
        // console.log(`\t >> tileBounds (${tileKey}) 的坐标: nw=`, nw, `se=`, se);

        // 计算切片中心点
        const tileCenter = {
            lat: (nw.lat + se.lat) / 2,
            lng: (nw.lng + se.lng) / 2
        };
        // console.log(`\t >> tileCenter (${tileKey}) 的坐标: `, tileCenter);

        // 计算切片中心点相对于截图中心点的偏移（米）
        const deltaLat = tileCenter.lat - centerLat;
        const deltaLng = tileCenter.lng - centerLng;

        // 转换偏移为米（近似，地球表面小区域）
        const deltaY = deltaLat * 111320; // approximately meters per degree latitude
        const deltaX = deltaLng * ((40075016.686 * Math.cos(this.tileSystem.toRadians(centerLat))) / 360); // meters per degree longitude
        // console.log(`\t >> deltaX|deltaY (${tileKey}) 的坐标: deltaX = `, deltaX, ' deltaY = ', deltaY);

        // 转换为像素偏移
        const pixelX = deltaX / scale + imageWidth / 2;
        const pixelY = -deltaY / scale + imageHeight / 2;
        // console.log(`\t >> pixelX|pixelY (${tileKey}) 的坐标: pixelX = `, pixelX, ' pixelY = ', pixelY);
        // console.log('\t----------------------------------\n');
        return {
            pixelX,
            pixelY
        };
    }

    async splitImage(image, rects, zoom, outputDir) {
        try {
            const { width: imgWidth, height: imgHeight } = await image.metadata();

            for (let i = 0; i < rects.length; i++) {
                const { idx, idy, x, y, width, height } = rects[i];
                if (width <= 0 || height <= 0) {
                    console.log(`Rectangle ${i} has non-positive dimensions, skipping...`);
                    continue;
                }

                // 计算裁剪区域，确保不超出图像边界
                const left = Math.max(0, x);
                const top = Math.max(0, y);
                const right = Math.min(imgWidth, x + width);
                const bottom = Math.min(imgHeight, y + height);

                if (right <= left || bottom <= top) {
                    console.log(`Rectangle [${left}, ${top}, ${right}, ${bottom}] does not intersect with image, skipping...`);
                    continue;
                }

                const cropWidth = right - left;
                const cropHeight = bottom - top;

                const extractRegion = {
                    left,
                    top,
                    width: cropWidth,
                    height: cropHeight
                };

                const extracted = image.clone().extract(extractRegion);

                // 计算偏移量
                const offsetX = x < 0 ? -x : 0;
                const offsetY = y < 0 ? -y : 0;

                // 创建一个透明背景的图像
                const compositeImage = sharp({
                    create: {
                        width: width,
                        height: height,
                        channels: 4,
                        background: { r: 0, g: 0, b: 0, alpha: 0 }
                    }
                });

                // 将裁剪后的图像合成到透明背景上
                const outputImagePath = path.join(outputDir, `zoom${zoom}`, `${idx}`, `${idy}.png`);
                await fs.mkdir(path.dirname(outputImagePath), { recursive: true });

                // 获取当前图像的 Buffer
                const currentImageBuffer = await compositeImage
                    .composite([{ input: await extracted.toBuffer(), left: offsetX, top: offsetY }])
                    .png()
                    .toBuffer();

                // 判断是否需要更新本地图像
                console.log(`\t处理[${idx}-${idy}]`);
                await this.updateLocalImage(outputImagePath, currentImageBuffer, this.tileSystem.tileSize);
            }
            console.log('All images processed successfully!');
        } catch (error) {
            console.error('Error processing the image:', error);
        }
    }

    /**
     * 更新本地图像
     * @param {string} outputImagePath - 本地图像路径
     * @param {Buffer} currentImageBuffer - 当前图像的 Buffer
     * @param {number} tileSize - 预设的瓦片大小
     */
    async updateLocalImage(outputImagePath, currentImageBuffer, tileSize) {
        try {
            // 检查本地图片是否存在
            const localImageExists = await fs
                .access(outputImagePath)
                .then(() => true)
                .catch(() => false);

            if (localImageExists) {
                // 读取本地图片
                const localImageBuffer = await fs.readFile(outputImagePath);

                // 检查本地图片是否完整
                const isLocalImageComplete = await this.isImageComplete(localImageBuffer, tileSize);

                // 如果本地图片已经是完整的，则不需要更新
                if (isLocalImageComplete) {
                    console.log(`\tTile is complete, skipping...\n`);
                    return;
                }

                // 本地图片不完整，合并本地图片和当前图片
                const mergedImage = sharp({
                    create: {
                        width: tileSize,
                        height: tileSize,
                        channels: 4,
                        background: { r: 0, g: 0, b: 0, alpha: 0 }
                    }
                });

                // 合并本地图片和当前图片
                const mergedBuffer = await mergedImage
                    .composite([
                        { input: localImageBuffer, left: 0, top: 0 },
                        { input: currentImageBuffer, left: 0, top: 0 }
                    ])
                    .png()
                    .toBuffer();

                // 保存合并后的图片
                await sharp(mergedBuffer).toFile(outputImagePath);
                console.log(`\tTile updated and merged complete\n`);
            } else {
                // 本地没有图片，直接保存当前图片
                await sharp(currentImageBuffer).toFile(outputImagePath);
                console.log(`\tTile image not exist, saved\n`);
            }
        } catch (error) {
            // 本地没有图片，直接保存当前图片
            await sharp(currentImageBuffer).toFile(outputImagePath);
            console.log(`\tTile image not exist, saved\n`);
        }
    }

    /**
     * 检查图片是否完整
     * @param {Buffer} imageBuffer - 图片的 Buffer
     * @param {number} tileSize - 预设的瓦片大小
     * @returns {Promise<boolean>} - 图片是否完整
     */
    async isImageComplete(imageBuffer, tileSize) {
        try {
            const image = sharp(imageBuffer);
            const { width, height, channels } = await image.metadata();

            // 检查图片的尺寸是否正确
            if (width !== tileSize || height !== tileSize) {
                return false;
            }

            // 检查图片的透明度
            const { data } = await image.raw().toBuffer({ resolveWithObject: true });
            for (let i = 0; i < data.length; i += channels) {
                const alpha = data[i + 3]; // 透明度通道
                if (alpha === 0) {
                    return false;
                }
            }

            return true;
        } catch (error) {
            console.error('Error checking image completeness:', error);
            return false;
        }
    }

    /**
     * 处理单个截图
     * @param {String} imagePath - 截图路径
     * @param {Object} metadata - 截图元数据 { centerLat, centerLng, zoom }
     * @param {String} outputDir - 输出图幅目录
     */
    async processScreenshot(imagePath, metadata, outputDir) {
        const { centerLat, centerLng, zoom } = metadata;
        try {
            // 读取图像缓冲区一次，避免重用同一实例
            // const imageBuffer = await fs.readFile(imagePath);
            // const image = sharp(imageBuffer);
            const imageBuffer = await fs.readFile(imagePath);
            const image = sharp(imageBuffer).toFormat('png');
            const metadataInfo = await image.metadata();
            const { width: imageWidth, height: imageHeight } = metadataInfo;
            console.log('imageWidth = ', imageWidth, 'imageHeight = ', imageHeight);

            // 获取每像素的地面距离
            const scale = this.tileSystem.getMetersPerPixel(centerLat, zoom);
            if (!scale || typeof scale !== 'number') {
                console.error(`无法计算每像素的地面距离（scale） for zoom level ${zoom} and latitude ${centerLat}`);
                this.missingTiles.push({ zoom, x: null, y: null, reason: '无法计算 scale' });
                return;
            }
            console.log('scale = ', scale);

            // 计算覆盖的切片范围
            const tileRange = this.tileSystem.calculateTileCoverage(centerLat, centerLng, zoom, imageWidth, imageHeight, scale);
            const { minX, maxX, minY, maxY } = tileRange;

            console.log('\n=========================================');
            console.log(`处理截图: ${imagePath}`);
            console.log(`切片范围 X: ${minX} - ${maxX}, Y: ${minY} - ${maxY}`);
            console.log(`切片覆盖 X: ${maxX - minX}, Y: ${maxY - minY}`);

            let coords_x = [];
            let coords_y = [];

            // 获取拆分后的tiles的横坐标
            for (let x = minX; x <= maxX; x++) {
                let center = this.getTileCenter(centerLat, centerLng, zoom, imageWidth, imageHeight, x, minY, scale);
                coords_x.push({
                    idx: x,
                    px: center.pixelX
                });
            }

            // 获取拆分后的tiles的纵坐标
            for (let y = minY; y <= maxY; y++) {
                let center = this.getTileCenter(centerLat, centerLng, zoom, imageWidth, imageHeight, minX, y, scale);
                coords_y.push({
                    idy: y,
                    py: center.pixelY
                });
            }

            // 构建所有的tiles坐标
            // console.log('coords_x', coords_x);
            // console.log('coords_y', coords_y);
            let rect_size = this.tileSystem.tileSize;
            let centers = [];
            let tiles = [];
            for (let i = 0; i < coords_x.length; i++) {
                for (let j = 0; j < coords_y.length; j++) {
                    centers.push({
                        idx: coords_x[i].idx,
                        idy: coords_y[j].idy,
                        x: coords_x[i].px,
                        y: coords_y[j].py
                    });
                }
            }

            for (let i = 0; i < coords_x.length; i++) {
                for (let j = 0; j < coords_y.length; j++) {
                    tiles.push({
                        idx: coords_x[i].idx,
                        idy: coords_y[j].idy,
                        x: Math.floor(coords_x[i].px - rect_size / 2),
                        y: Math.floor(coords_y[j].py - rect_size / 2),
                        width: rect_size,
                        height: rect_size
                    });
                }
            }
            // console.log(JSON.stringify(centers));
            // console.log(JSON.stringify(tiles));

            // 拆分图像到具体的目录中
            await this.splitImage(image, tiles, zoom, outputDir);
        } catch (err) {
            console.error('处理单个截图失败:', err);
            this.missingTiles.push({ zoom, x, y, reason: '处理失败', error: err.message });
        }
    }

    /**
     * 批量处理截图
     * @param {Array} screenshots - 截图数组 [{ imagePath, metadata }]
     * @param {String} outputDir - 输出图幅目录
     */
    async batchProcess(screenshots, outputDir) {
        for (const screenshot of screenshots) {
            const { imagePath, metadata } = screenshot;

            this.app.server.broadcast(
                JSON.stringify({
                    event: 'import_status',
                    data: `开始处理截图：${path.basename(imagePath)}`
                })
            );
            // console.log(`\n开始处理截图: ${imagePath}`);
            await this.processScreenshot(imagePath, metadata, outputDir);
        }

        // 总结缺失的切片
        if (this.missingTiles.length > 0) {
            console.warn('\n以下切片在处理过程中缺失或失败:');
            this.missingTiles.forEach((tile) => {
                console.warn(`- Zoom: ${tile.zoom}, X: ${tile.x !== null ? tile.x : 'N/A'}, Y: ${tile.y !== null ? tile.y : 'N/A'}, 原因: ${tile.reason}`);
                if (tile.error) {
                    console.warn(`  错误信息: ${tile.error}`);
                }
            });
        } else {
            console.log('\n所有切片已成功处理。');
        }

        this.app.server.broadcast(
            JSON.stringify({
                event: 'import_end',
                data: `所有切片已成功处理。`
            })
        );
    }
}

module.exports = MapTileManager;
